package chapt07.imaging;
// Triangle.java
// OpenGL SuperBible
// Demonstrates Imaging Operations
// Program by Richard S. Wright Jr.

import java.io.IOException;

import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;

import com.jogamp.opengl.util.texture.spi.TGAImage;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

public class Imaging
{
	static GLU glu = new GLU();
	
	//////////////////////////////////////////////////////////////////
	// Module globals to save source image data
	static TGAImage tgaImage;
	static ByteBuffer pImage = null;
	static int iWidth, iHeight, iComponents;
	static int eFormat;
	
	// Global variable to store desired drawing mode
	static int iRenderMode = 1;
	static boolean bHistogram = false;
	
	// Inverted color table
	static byte invertTable[] = new byte[256 * 3];
	
	// Do a black and white scaling
	static float lumMat[] = {
		0.30f, 0.30f, 0.30f, 0.0f,
		0.59f, 0.59f, 0.59f, 0.0f,
		0.11f, 0.11f, 0.11f, 0.0f,
		0.0f,  0.0f,  0.0f,  1.0f
	};
	
	// Sharpen convolution kernel
	static float mSharpen[] = {
		0.0f, -1.0f, 0.0f,
		-1.0f, 5.0f, -1.0f,
		0.0f, -1.0f, 0.0f
	};
	
	// Emboss convolution kernel
	static float mEmboss[] = {
		2.0f, 0.0f, 0.0f,
		0.0f, -1.0f, 0.0f,
		0.0f, 0.0f, -1.0f
	};
	
	// Storeage for histogram statistics
	static int histoGram[] = new int [256];
	
	static int processMenuValue = 1;
		
	///////////////////////////////////////////////////////////////////////////////
	// Reset flags as appropriate in response to menu selections
	public static void processMenu(GL2 gl2, int value)
	{
		// For histogram, do not change render mode, just set
	    // histogram flag to true
		if(value == 6) // Histogram
		{
			bHistogram = true;
			return;
		}
		
		if(value == 0)
		{
			try
			{
				tgaImage.write("Screenshot.tga");
			}
			catch(IOException e)
			{
				System.exit(0);
			}
		}
		else
		{
			// Change render mode index to match menu entry index
			iRenderMode = value;
		}
	}
	
	//////////////////////////////////////////////////////////////////
	// This function does any needed initialization on the rendering
	// context.
	protected static void setupRC(GL2 gl2, int width, int height)
	{
		// Black background
		gl2.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
		
		// Load the horse image
		gl2.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
		try
		{
			tgaImage = TGAImage.read(Imaging.class.getResourceAsStream("horse.tga"));
			iWidth = tgaImage.getWidth();
			iHeight = tgaImage.getHeight();
			eFormat = tgaImage.getGLFormat();
			pImage = tgaImage.getData();
		}
		catch(IOException e)
		{
			System.exit(0);
		}
	}
	
	///////////////////////////////////////////////////////////////////////        
	// Called to draw scene
	protected static void renderScene(GL2 gl2, int w, int h)
	{
		processMenu(gl2, processMenuValue);
		
		int i;                        // Looping variable
		int iViewport[] = new int[4]; // Viewport
		int iLargest;                 // Largest histogram value
		
		// Clear the window with current clearing color
		gl2.glClear(GL2.GL_COLOR_BUFFER_BIT);
		
		// Current Raster Position always at bottom left hand corner of window
		gl2.glRasterPos2i(0, 0);
		gl2.glGetIntegerv(GL2.GL_VIEWPORT, iViewport, 0);
		gl2.glPixelZoom((float)iViewport[2] / (float)iWidth, (float)iViewport[3] / (float)iHeight);
		
		if(bHistogram == true) // Collect Histogram data
		{
			// We are collecting luminance data, use our conversion formula
	        // instead of OpenGL's (which just adds color components together)
			gl2.glMatrixMode(GL2.GL_COLOR);
			gl2.glLoadMatrixf(lumMat, 0);
			gl2.glMatrixMode(GL2.GL_MODELVIEW);
			
			// Start collecting histogram data, 256 luminance values
			gl2.glHistogram(GL2.GL_HISTOGRAM, 256, GL2.GL_LUMINANCE, false);
			gl2.glEnable(GL2.GL_HISTOGRAM);
		}
		
		// Do image operation, depending on rendermode index
		switch(iRenderMode)
		{
			case 5: // Sharpen image
				gl2.glConvolutionFilter2D(GL2.GL_CONVOLUTION_2D, GL2.GL_RGB, 3, 3, GL2.GL_LUMINANCE, GL2.GL_FLOAT, FloatBuffer.wrap(mSharpen));
				gl2.glEnable(GL2.GL_CONVOLUTION_2D);
				break;
				
			case 4: // Emboss image
				gl2.glConvolutionFilter2D(GL2.GL_CONVOLUTION_2D, GL2.GL_RGB, 3, 3, GL2.GL_LUMINANCE, GL2.GL_FLOAT, FloatBuffer.wrap(mEmboss));
				gl2.glEnable(GL2.GL_CONVOLUTION_2D);
				gl2.glMatrixMode(GL2.GL_COLOR);
				gl2.glLoadMatrixf(lumMat, 0);
				gl2.glMatrixMode(GL2.GL_MODELVIEW);
				break;
			
			case 3: // Invert Image
				for(i = 0; i < 255; i++)
				{
					invertTable[i * 3 + 0] = (byte)(255 - i);
					invertTable[i * 3 + 1] = (byte)(255 - i);
					invertTable[i * 3 + 2] = (byte)(255 - i);
				}
				
				gl2.glColorTable(GL2.GL_COLOR_TABLE, GL2.GL_RGB, 256, GL2.GL_RGB, GL2.GL_UNSIGNED_BYTE, ByteBuffer.wrap(invertTable));
				gl2.glEnable(GL2.GL_COLOR_TABLE);
				break;
				
			case 2: // Brighten Image
				gl2.glMatrixMode(GL2.GL_COLOR);
				gl2.glScalef(1.25f, 1.25f, 1.25f);
				gl2.glMatrixMode(GL2.GL_MODELVIEW);
				break;
			
			case 1: // Just do a plain old image copy
			default:
				// This line intentially left blank
				break;
		}
		
		// Do the pixel draw
		gl2.glDrawPixels(iWidth, iHeight, eFormat, GL2.GL_UNSIGNED_BYTE, pImage);
		
		// Fetch and draw histogram?
		if(bHistogram == true)
		{
			// Read histogram data into buffer
			gl2.glGetHistogram(GL2.GL_HISTOGRAM, true, GL2.GL_LUMINANCE, GL2.GL_INT, IntBuffer.wrap(histoGram));
			
			// Find largest value for scaling graph down
			iLargest = 0;
			for(i = 0; i < 255; i++)
				if(iLargest < histoGram[i])
					iLargest = histoGram[i];
			
			// White lines
			gl2.glColor3f(1.0f, 1.0f, 1.0f);
			gl2.glBegin(GL2.GL_LINE_STRIP);
				for(i = 0; i < 255; i++)
					gl2.glVertex2f((float)i, (float)histoGram[i] / (float)iLargest * 128.0f);
			gl2.glEnd();
			
			bHistogram = false;
			gl2.glDisable(GL2.GL_HISTOGRAM);
		}
		
		// Reset everything to default
		gl2.glMatrixMode(GL2.GL_COLOR);
		gl2.glLoadIdentity();
		gl2.glMatrixMode(GL2.GL_MODELVIEW);
		gl2.glDisable(GL2.GL_CONVOLUTION_2D);
		gl2.glDisable(GL2.GL_COLOR_TABLE);
		
		// Show our hard work...
		gl2.glFlush();
	}
	
	public static void changeSize(GL2 gl2, int w, int h)
	{
	    // Prevent a divide by zero, when window is too short
	    // (you cant make a window of zero width).
		if(h == 0)
			h = 1;

		gl2.glViewport(0, 0, w, h);
		
		// Reset the coordinate system before modifying
		gl2.glMatrixMode(GL2.GL_PROJECTION);
		gl2.glLoadIdentity();
		
		// Set the clipping volume
		glu.gluOrtho2D(0.0f, (float)w, 0.0, (float)h);
		
		gl2.glMatrixMode(GL2.GL_MODELVIEW);
		gl2.glLoadIdentity();		
	}
}